/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.architexa.org.eclipse.gef.editparts;

import com.architexa.org.eclipse.draw2d.ChopboxAnchor;
import com.architexa.org.eclipse.draw2d.Connection;
import com.architexa.org.eclipse.draw2d.ConnectionAnchor;
import com.architexa.org.eclipse.draw2d.IFigure;
import com.architexa.org.eclipse.draw2d.PolylineConnection;
import com.architexa.org.eclipse.draw2d.XYAnchor;
import com.architexa.org.eclipse.draw2d.geometry.Point;
import com.architexa.org.eclipse.gef.AccessibleAnchorProvider;
import com.architexa.org.eclipse.gef.ConnectionEditPart;
import com.architexa.org.eclipse.gef.DragTracker;
import com.architexa.org.eclipse.gef.EditPart;
import com.architexa.org.eclipse.gef.GraphicalEditPart;
import com.architexa.org.eclipse.gef.LayerConstants;
import com.architexa.org.eclipse.gef.NodeEditPart;
import com.architexa.org.eclipse.gef.Request;
import com.architexa.org.eclipse.gef.tools.SelectEditPartTracker;

import java.util.ArrayList;
import java.util.List;



/**
 * The base implementation for {@link com.architexa.org.eclipse.gef.ConnectionEditPart}.
 */
public abstract class AbstractConnectionEditPart
	extends AbstractGraphicalEditPart
	implements ConnectionEditPart, LayerConstants
{

private static final ConnectionAnchor DEFAULT_SOURCE_ANCHOR =
	new XYAnchor(new Point(10, 10));
private static final ConnectionAnchor DEFAULT_TARGET_ANCHOR =
	new XYAnchor(new Point(100, 100));

/**
 * Provides accessibility support for when connections are also themselves nodes. If a
 * connection is the source or target of another connection, then its midpoint is used as
 * the accessible anchor location.
 * @author hudsonr
 * @since 2.0
 */
protected final class DefaultAccessibleAnchorProvider
	implements AccessibleAnchorProvider
{
	/**
	 * This class is internal, but is made protected so that JavaDoc will see it.
	 */
	DefaultAccessibleAnchorProvider() { }
	/**
	 * @see AccessibleAnchorProvider#getSourceAnchorLocations()
	 */
	public List getSourceAnchorLocations() {
		List list = new ArrayList();
		if (getFigure() instanceof Connection) {
			Point p = ((Connection)getFigure())
					.getPoints()
					.getMidpoint();
			getFigure().translateToAbsolute(p);
			list.add(p);
		}
		return list;
	}
	/**
	 * @see AccessibleAnchorProvider#getTargetAnchorLocations()
	 */
	public List getTargetAnchorLocations() {
		return getSourceAnchorLocations();
	}
}

private EditPart
		sourceEditPart,
		targetEditPart;

/**
 * Activates the Figure representing this, by setting up
 * the start and end connections, and adding the figure
 * to the Connection Layer.
 * 
 * @see #deactivate()
 */
protected void activateFigure() {
	getLayer(CONNECTION_LAYER).add(getFigure());
}

/**
 * @see com.architexa.org.eclipse.gef.EditPart#addNotify()
 */
public void addNotify() {
	activateFigure();
	super.addNotify();
}

/**
 * Returns a newly created Figure to represent these type of
 * EditParts.
 *
 * @return  The created Figure.
 */
protected IFigure createFigure() {
	return new PolylineConnection();
}

/**
 * Deactivates the Figure representing this, by removing
 * it from the connection layer, and resetting the 
 * source and target connections to <code>null</code>.
 */
protected void deactivateFigure() {
	getLayer(CONNECTION_LAYER).remove(getFigure());
	getConnectionFigure().setSourceAnchor(null);
	getConnectionFigure().setTargetAnchor(null);
}

/**
 * <code>AbstractConnectionEditPart</code> extends getAdapter() to overrides the {@link
 * AccessibleAnchorProvider} adapter returned by the superclass.  When treating a
 * connection as a node for other connections, it makes sense to target its midpoint, and
 * not the edge of its bounds.
 * @see AbstractConnectionEditPart.DefaultAccessibleAnchorProvider
 * @see AbstractGraphicalEditPart#getAdapter(Class)
 * @param adapter the adapter Class
 * @return the adapter
 */
public Object getAdapter(Class adapter) {
	if (adapter == AccessibleAnchorProvider.class)
		return new DefaultAccessibleAnchorProvider();
	return super.getAdapter(adapter);
}

/**
 * Convenience method for casting this GraphicalEditPart's Figure to a {@link Connection}
 * @return the Figure as a Connection
 */
public Connection getConnectionFigure() {
	return (Connection)getFigure();
}

/**
 * @see com.architexa.org.eclipse.gef.EditPart#getDragTracker(Request)
 */
public DragTracker getDragTracker(Request req) {
	return new SelectEditPartTracker(this);
}

/**
 * @see com.architexa.org.eclipse.gef.ConnectionEditPart#getSource()
 */
public EditPart getSource() {
	return sourceEditPart;
}

/**
 * @see com.architexa.org.eclipse.gef.ConnectionEditPart#getTarget()
 */
public EditPart getTarget() {
	return targetEditPart;
}

/**
 * Returns the <code>ConnectionAnchor</code> for the <i>source</i> end of the connection. 
 * If the source is an instance of {@link NodeEditPart}, that interface will be used to
 * determine the proper ConnectionAnchor. If the source is not an instance of
 * <code>NodeEditPart</code>, this method should be overridden to return the correct
 * ConnectionAnchor. Failure to do this will cause a default anchor to be used so that the
 * connection figure will be made visible to the developer.
 * @return ConnectionAnchor for the source end of the Connection
 */
protected ConnectionAnchor getSourceConnectionAnchor() {
	if (getSource() != null) {
		if (getSource() instanceof NodeEditPart) {
			NodeEditPart editPart = (NodeEditPart) getSource();
			return editPart.getSourceConnectionAnchor(this);
		}
		IFigure f = ((GraphicalEditPart)getSource()).getFigure();
		return new ChopboxAnchor(f);
	}
	return DEFAULT_SOURCE_ANCHOR;
}

/**
 * Returns the <code>ConnectionAnchor</code> for the <i>target</i> end of the connection. 
 * If the target is an instance of {@link NodeEditPart}, that interface will be used to
 * determine the proper ConnectionAnchor. If the target is not an instance of
 * <code>NodeEditPart</code>, this method should be overridden to return the correct
 * ConnectionAnchor. Failure to do this will cause a default anchor to be used so that the
 * connection figure will be made visible to the developer.
 * @return ConnectionAnchor for the target end of the Connection
 */
protected ConnectionAnchor getTargetConnectionAnchor() {
	if (getTarget() != null) {
		if (getTarget() instanceof NodeEditPart) {
			NodeEditPart editPart = (NodeEditPart) getTarget();
			return editPart.getTargetConnectionAnchor(this);
		}
		IFigure f = ((GraphicalEditPart)getTarget()).getFigure();
		return new ChopboxAnchor(f);
	}
	return DEFAULT_TARGET_ANCHOR;
}

/**
 * Extended here to also refresh the ConnectionAnchors.
 * @see com.architexa.org.eclipse.gef.EditPart#refresh()
 */
public void refresh() {
	refreshSourceAnchor();
	refreshTargetAnchor();
	super.refresh();
}

/**
 * Updates the source ConnectionAnchor. Subclasses should override {@link
 * #getSourceConnectionAnchor()} if necessary, and not this method.
 */
protected void refreshSourceAnchor() {
	getConnectionFigure().setSourceAnchor(getSourceConnectionAnchor());
}

/**
 * Updates the target ConnectionAnchor. Subclasses should override {@link
 * #getTargetConnectionAnchor()} if necessary, and not this method.
 */
protected void refreshTargetAnchor() {
	getConnectionFigure().setTargetAnchor(getTargetConnectionAnchor());
}

/**
 * Extended here to remove the ConnectionEditPart's connection figure from the connection
 * layer.
 * @see com.architexa.org.eclipse.gef.EditPart#removeNotify()
 */
public void removeNotify() {
	deactivateFigure();
	super.removeNotify();
}

/**
 * Extended to implement automatic addNotify and removeNotify handling.
 * @see com.architexa.org.eclipse.gef.EditPart#setParent(EditPart)
 */
public void setParent(EditPart parent) {
	boolean wasNull = getParent() == null;
	boolean becomingNull = parent == null;
	if (becomingNull && !wasNull)
		removeNotify();
	super.setParent(parent);
	if (wasNull && !becomingNull)
		addNotify();
}

/**
 * Sets the source EditPart of this connection.
 *
 * @param editPart  EditPart which is the source.
 */
public void setSource(EditPart editPart) {
	if (sourceEditPart == editPart)
		return;
	sourceEditPart = editPart;
	if (sourceEditPart != null)
		setParent(sourceEditPart.getRoot());
	else if (getTarget() == null)
		setParent(null);
	if (sourceEditPart != null && targetEditPart != null)
		refresh();
}

/**
 * Sets the target EditPart of this connection.
 *
 * @param editPart  EditPart which is the target.
 */
public void setTarget(EditPart editPart) {
	if (targetEditPart == editPart)
		return;
	targetEditPart = editPart;
	if (editPart != null)
		setParent(editPart.getRoot());
	else if (getSource() == null)
		setParent(null);
	if (sourceEditPart != null && targetEditPart != null)
		refresh();
}

}
